#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <semaphore.h>
#include <pthread.h>

//1. 单生产单消费---只需要维护生产者与消费者之间的关系
/*
template <typename T>
class RingQueue
{
public:
    void P(sem_t& sem)
    {
        sem_wait(&sem);//信号量内部维护的计数器--;
    }

    void V(sem_t& sem)
    {
        sem_post(&sem);//信号量内部维护的计数器++;
    }

public:
    RingQueue(int capacity)
    :_ring_queue(capacity)
    ,_capacity(capacity)
    {
        _consumer_step=0;
        _productor_step=0;
        sem_init(&(_room_sem),0,_capacity);//最初阶段,空间资源为数组容量
        sem_init(&(_data_sem),0,0);//最初阶段,数据资源为0
    }

    void EnQueue(const T& in)//生产者向环形队列中存放数据
    {
        //采用信号量申请空间资源---p操作---p(_room_sem)
        P(_room_sem);
        //申请空间资源成功,向下运行;申请空间资源失败,阻塞等待;

        //必定存在空间资源存放数据,存放数据
        _ring_queue[_productor_step]=in;
        _productor_step++;
        _productor_step %= _capacity;

        //数据资源增加1份,描述数据资源的信号量内部计数器++;
        V(_data_sem);
    }
    void DeQueue(T* out)//消费者从环形队列中获取数据
    {
        //采用信号量申请数据资源---p操作---p(_data_room)
        P(_data_sem);
        //申请数据资源成功,向下运行;申请数据资源失败,阻塞等待;

        //必定存在数据资源可以获取,获取数据资源
        *out=_ring_queue[_consumer_step];
        _consumer_step++;
        _consumer_step %= _capacity;

        //空间资源增加1份,描述空间资源的信号量内部计数器++;
        V(_room_sem);
    }

    ~RingQueue()
    {
        sem_destroy(&(_data_sem));
        sem_destroy(&(_room_sem));
    }
private:
    vector<T> _ring_queue;//环形队列底层结构:数组
    int _capacity;//环形队列的容量

    //生产者下标&&消费者下标---分配空间资源与数据资源的下标
    int _consumer_step;
    int _productor_step;

    //申请空间资源的信号量&&申请数据资源的信号量
    sem_t  _room_sem;//空间信号量---生产者
    sem_t  _data_sem;//数据信号量---消费者
};
*/

//多生产多消费模型

//生产者与消费者之间:互斥+同步
//(同步:当队列为空&&队列为满的两种状态,只能两者其中之一运行)
//(互斥: 最初阶段,即便消费者先运行,数据资源为0,消费者也会进行阻塞等待,只有生产者生产数据结束,对数据资源进行V操作,消费者才能被唤醒)

//生产者之间:互斥

//消费者之间:互斥

//实现方案:加锁
//环形队列的特点:当环形队列既不为空又不为满的情况下,生产者与消费者可以实现真正的并发

//1.生产者之间使用一把锁(_productor_mutex),生产者之间竞争同一把锁完成申请空间资源存放数据;
//2.消费者之间使用一把锁(_consumer_mutex),消费者之间竞争同一把锁完成申请数据资源获取数据;

//假设生产者1竞争_productor_mutex锁成功,消费者1竞争_consumer_mutex锁成功,此时生产者1线程与消费者1线程可以并发运行
//但是当环形队列为空或者为满的情况下,生产者1线程与消费者1线程需要完成同步！！！

//加锁方案1:
template <typename T>
class RingQueue
{
public:
    void P(sem_t& sem)
    {
        sem_wait(&sem);//信号量内部维护的计数器--;
    }

    void V(sem_t& sem)
    {
        sem_post(&sem);//信号量内部维护的计数器++;
    }
    
    //互斥锁禁止拷贝,引用传参
    void Lock(pthread_mutex_t& mutex)
    {
        pthread_mutex_lock(&(mutex));
    }

    void UnLock(pthread_mutex_t& mutex)
    {
        pthread_mutex_unlock(&(mutex));
    }

public:
    RingQueue(int capacity)
    :_ring_queue(capacity)
    ,_capacity(capacity)
    {
        _consumer_step=0;
        _productor_step=0;
        sem_init(&(_room_sem),0,_capacity);//最初阶段,空间资源为数组容量
        sem_init(&(_data_sem),0,0);//最初阶段,数据资源为0

        pthread_mutex_init(&(_productor_mutex),nullptr);
        pthread_mutex_init(&(_consumer_mutex),nullptr);
    }
    
    //加锁方案1:
    /*
    void EnQueue(const T& in)//生产者向环形队列中存放数据
    {
        //生产者竞争_productor_mutex;
        Lock(_productor_mutex);
        //采用信号量申请空间资源---p操作---p(_room_sem)
        P(_room_sem);
        //申请空间资源成功,向下运行;申请空间资源失败,阻塞等待;

        //必定存在空间资源存放数据,存放数据
        _ring_queue[_productor_step]=in;
        _productor_step++;
        _productor_step %= _capacity;

        //数据资源增加1份,描述数据资源的信号量内部计数器++;
        V(_data_sem);
        UnLock(_productor_mutex);
    }

    void DeQueue(T* out)//消费者从环形队列中获取数据
    {
        //消费者竞争_consumer_mutex;
        Lock(_consumer_mutex);
        //采用信号量申请数据资源---p操作---p(_data_room)
        P(_data_sem);
        //申请数据资源成功,向下运行;申请数据资源失败,阻塞等待;

        //必定存在数据资源可以获取,获取数据资源
        *out=_ring_queue[_consumer_step];
        _consumer_step++;
        _consumer_step %= _capacity;

        //空间资源增加1份,描述空间资源的信号量内部计数器++;
        V(_room_sem);
        UnLock(_consumer_mutex);
    }
    */
     
    //加锁方案2:各个线程先预定资源,有资源便不会进行阻塞等待,然后各个线程竞争锁,提升效率;
     void EnQueue(const T& in)//生产者向环形队列中存放数据
    {
        //采用信号量申请空间资源---p操作---p(_room_sem)
        P(_room_sem);
        //申请空间资源成功,向下运行;申请空间资源失败,阻塞等待;

        //生产者竞争_productor_mutex;
        Lock(_productor_mutex);
        

        //必定存在空间资源存放数据,存放数据
        _ring_queue[_productor_step]=in;
        _productor_step++;
        _productor_step %= _capacity;

        UnLock(_productor_mutex);
        //数据资源增加1份,描述数据资源的信号量内部计数器++;
        V(_data_sem);
        
    }

    void DeQueue(T* out)//消费者从环形队列中获取数据
    {
        
        //采用信号量申请数据资源---p操作---p(_data_room)
        P(_data_sem);
        //申请数据资源成功,向下运行;申请数据资源失败,阻塞等待;

        //消费者竞争_consumer_mutex;
        Lock(_consumer_mutex);

        //必定存在数据资源可以获取,获取数据资源
        *out=_ring_queue[_consumer_step];
        _consumer_step++;
        _consumer_step %= _capacity;

        UnLock(_consumer_mutex);

        //空间资源增加1份,描述空间资源的信号量内部计数器++;
        V(_room_sem);
        
    }

    ~RingQueue()
    {
        sem_destroy(&(_data_sem));
        sem_destroy(&(_room_sem));

        pthread_mutex_destroy(&(_productor_mutex));
        pthread_mutex_destroy(&(_consumer_mutex));
    }
private:
    vector<T> _ring_queue;//环形队列底层结构:数组
    int _capacity;//环形队列的容量

    //生产者下标&&消费者下标---分配空间资源与数据资源的下标
    int _consumer_step;
    int _productor_step;

    //申请空间资源的信号量&&申请数据资源的信号量
    sem_t  _room_sem;//空间信号量---生产者
    sem_t  _data_sem;//数据信号量---消费者

    //定义局部互斥锁,维护多生产与多消费之间的互斥关系
    pthread_mutex_t _productor_mutex;
    pthread_mutex_t _consumer_mutex;
};